﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using A2DFramework.EsbService.DistributedEsb.Exceptions;
using A2DFramework.EsbService.DistributedEsb.SessionSupport;

namespace A2DFramework.EsbService.DistributedEsb
{
    public class CommandHandlerProxy
    {
        private List<IRequestHandlerWorker> handlers = new List<IRequestHandlerWorker>();
        private Dictionary<Type, IRequestHandlerWorker> handlersMap = new Dictionary<Type, IRequestHandlerWorker>();
        private Dictionary<Type, MethodInfo> methodsMap = new Dictionary<Type, MethodInfo>();

        public void AddHandler(IRequestHandlerWorker commandHandler)
        {
            if(!this.handlers.Contains(commandHandler))
                this.handlers.Add(commandHandler);
        }
        public EsbResponseWrapper Handler(EsbRequestWrapper cmd, ISessionContext session)
        {
            MethodInfo mi;
            IRequestHandlerWorker handler;

            GetHandler(cmd, out mi, out handler);

            if(mi==null||handler==null)
                throw new CommandHandlerNotFoundException("Handler not found");
            return (EsbResponseWrapper)mi.Invoke(handler, new object[] { cmd });
        }
        private void GetHandler(EsbRequestWrapper cmd, out MethodInfo m, out IRequestHandlerWorker hdl)
        {
            m = null;
            hdl = null;

            Type targetType = cmd.GetType();
            if (handlersMap.ContainsKey(targetType) && methodsMap.ContainsKey(targetType))
            {
                m=methodsMap[targetType];
                hdl=handlersMap[targetType];
                return;
            }

            foreach (var handler in handlers)
            {
                foreach (var mi in handler.GetType().GetMethods().Where(t => t.Name.Equals("Handle")))
                {
                    bool thisMethod = false;
                    foreach (var pi in mi.GetParameters())
                    {
                        if (pi.ParameterType.Equals(targetType))
                        {
                            thisMethod = true;
                            break;
                        }
                    }
                    if (thisMethod)
                    {
                        hdl = handler;
                        m = mi;

                        handlersMap[targetType] = hdl;
                        methodsMap[targetType] = m;
                        return;
                    }
                }
            }
        }
    }
}
